﻿using System;
using CommunityServer.Blogs.Components;
using CommunityServer.Components;
using CommunityServer.Discussions.Components;
using CommunityServer.MediaGalleries.Components;
using CommunityServer.Spam;
using Joel.Net;
using Telligent.DynamicConfiguration.Components;

namespace BrianPeek.Akismet.CS
{
	public class AkismetRule : ConfigurableContentScorerBase
	{
		private static readonly Guid RuleId = new Guid("E87B1596-2D73-4562-A07E-52CD91E0DE86");
		private static readonly string DefaultKey = string.Empty;
		private const string Name = "Akismet Spam Filter";
		private const string Description = "A spam rule that uses the Akismet service to score comments.";
        private const int DefaultScore = 10;

		public override PropertyGroup[] GetPropertyGroups()
		{
            PropertyGroup[] propertyGroups = new[] { new PropertyGroup("akismetSettings", "Akismet Spam Settings", 1) };

            propertyGroups[0].Properties.Add(new Property("score", "Score", PropertyType.Int, 0, DefaultScore.ToString()));
            propertyGroups[0].Properties.Add(new Property("key", "Site Key", PropertyType.String, 1, DefaultKey));

            return propertyGroups;
		}

		public override bool TryGetScore(ScoreableContent scoreableContent, out int score)
		{
			score = 0;

			try
			{
				string apiKey = GetStringValue("key", string.Empty);
				int scoreVal = GetIntValue("score", 0);

				if(!string.IsNullOrEmpty(apiKey))
				{
					string siteInstance;

					AkismetComment comment = GetAkismetComment(scoreableContent, out siteInstance);

					if(comment == null)
						return false;

					Joel.Net.Akismet api = new Joel.Net.Akismet(apiKey, siteInstance, "Teligent Community Server/2008.5 | BrianPeek.Akismet.CS/1.0");
					bool validKey = api.VerifyKey();

					if(validKey)
					{
						bool spam = api.CommentCheck(comment);
						if(spam)
						{
							EventLogs.Info("Akismet determined a post is spam, scoring it (" + scoreVal + "): " + comment, Name, 9001);
							score = scoreVal;
							return true;
						}
					}
					else
					{
						EventLogs.Warn("Akismet API key is invalid", Name, 9002);
						return false;
					}
				}
			}
			catch(Exception ex)
			{
				EventLogs.Warn(ex.ToString(), ex.Message, 9002);
				return false;
			}

			return true;
		}

		public override Guid ContentScorerId
		{
			get { return RuleId; }
		}

		public override string ScorerName
		{
			get { return Name; }
		}

		public override string ScorerDescription
		{
			get { return Description; }
		}

        public static AkismetComment GetAkismetComment(ScoreableContent content, out string instance)
        {
			Post post = content.SourceObject as Post;

			if(post == null)
			{
				instance = null;
				return null;
			}

			AkismetComment comment = new AkismetComment();
			string siteInstance = string.Empty;
			CSContext context = CSContext.Current;
			string baseUrl = context.SiteSettings.SiteDomainUrl;

			if (context.Context.Request.UrlReferrer != null)
				comment.Referrer = context.Context.Request.UrlReferrer.ToString();
			comment.UserAgent = context.Context.Request.UserAgent;
			comment.UserIp = GetUserHostAddress();
       		comment.CommentContent = post.Body;

    		// Weblog Feedback
    		WeblogPost weblogPost = post as WeblogPost;
    		if ((weblogPost != null) && weblogPost.IsFeedback)
    		{
    			comment.CommentAuthor = weblogPost.SubmittedUserName;
    			if (!string.IsNullOrEmpty(weblogPost.AuthorUrl))
    				comment.CommentAuthorUrl = weblogPost.AuthorUrl;
    			if (weblogPost.BlogPostType == BlogPostType.Comment)
    				comment.CommentType = "comment";
    			else if (weblogPost.BlogPostType == BlogPostType.Trackback)
    				comment.CommentType = "trackback";

    			BlogUrls blogUrls = BlogUrls.Instance();
    			WeblogPost parentPost = WeblogPosts.GetPost(weblogPost.ParentID, false, true, false);
    			comment.Permalink = baseUrl + blogUrls.Post(parentPost);

    			siteInstance = baseUrl + blogUrls.HomePage(post.Section.ApplicationKey);
    		}

    		// Forum Post
    		ForumPost forumPost = post as ForumPost;
    		if (forumPost != null)
			{
				comment.CommentAuthor = forumPost.User.DisplayName ?? "Anonymous";
				comment.CommentType = "comment";

				ForumUrls forumUrls = ForumUrls.Instance();
				comment.Permalink = baseUrl + forumUrls.PostPermaLink(forumPost.ThreadID, forumPost.PostID);
				siteInstance = baseUrl + forumUrls.Forum(forumPost.Section.SectionID);
			}

			// Media Gallery Post
			MediaGalleryPost mediaPost = post as MediaGalleryPost;
			if ((mediaPost != null) && (mediaPost.MediaGalleryPostType == MediaGalleryPostType.Comment))
			{
				comment.CommentAuthor = mediaPost.SubmittedUserName;
				if (!string.IsNullOrEmpty(mediaPost.AuthorURL))
					comment.CommentAuthorUrl = mediaPost.AuthorURL;
				comment.CommentType = "comment";

				MediaGalleryUrls galleryUrls = MediaGalleryUrls.Instance();
				comment.Permalink = baseUrl + galleryUrls.ViewMediaGalleryPost(mediaPost.Section.ApplicationKey, mediaPost.ParentID);

				siteInstance = baseUrl + galleryUrls.ViewMediaGallery(mediaPost.Section.ApplicationKey);
			}
			instance = siteInstance;
			return comment;
        }

        public static string GetUserHostAddress()
        {
            CSContext context = CSContext.Current;
            string result = context.Context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (string.IsNullOrEmpty(result))
                return context.Context.Request.UserHostAddress;

            int pos = result.IndexOf(",", StringComparison.InvariantCulture);
            if (pos > 0)
                return result.Substring(0, pos);
            else
                return result;
        }
	}
}
